<?php
   /*
   
   $Id: auto_backup_db.php, v 4.1 22/03/2012 

   Released under the GNU General Public License

   If wished, add a cron job like: php -n $HOME/public_html/catalog/admin/includes/auto_backup_db.php > $HOME/public_html/catalog/admin/backups/backup.log
   Rename admin to your admin dir name & remove catalog if your shop is in the root
   
   */

   // initialise dBase etc if cron job
   if(!defined('DB_DATABASE'))
   {
      $cron = true;
      require ('configure.php');
      chdir(DIR_FS_ADMIN);
      require(DIR_WS_FUNCTIONS . 'database.php');
      tep_db_connect() or die('Unable to connect to database server!');
      require(DIR_WS_FUNCTIONS . 'general.php');
      require(DIR_WS_INCLUDES . 'database_tables.php');
      $configuration_query = tep_db_query('select configuration_key as cfgKey, configuration_value as cfgValue from ' . TABLE_CONFIGURATION);
      while ($configuration = tep_db_fetch_array($configuration_query)) define($configuration['cfgKey'], $configuration['cfgValue']);
      include(DIR_WS_CLASSES . 'language.php');
      $lng = new language();
      if (isset($_GET['language']) && tep_not_null($_GET['language'])) $lng->set_language($_GET['language']);
      $language = $lng->language['directory'];
   }
   include(DIR_WS_LANGUAGES . $language . '/auto_backup.php');
   // messages
   function at_message($message, $class='error', $lnk=false, $sess=false, $crm='')
   {
      global $cron, $messageStack;
      if (function_exists('tep_href_link')) $link = $lnk === true ? ' -- <a href="' . tep_href_link(FILENAME_BACKUP) . '">'.DBASE_MANAGER.'</a>' : $lnk;
      if ($cron) echo $message . "\n\n" . $crm;
      elseif ($sess) $messageStack->add_session($message . $link, $class);
      else $messageStack->add($message . $link, $class);
   }
   // time
   function at_microtime_float()
   {
      list($usec, $sec) = explode(" ",microtime());
      return ((float)$usec + (float)$sec);
   }
   //create sort func
   function at_comp($a, $b)
   {
      return ($a['date'] < $b['date']) ? -1 : 1;
   }
   // gzip
   function at_gzip($file)
   {
      $level = 6;
      $out = $file . '.gz';
      if (!file_exists ($file) || !is_readable ($file)) return false;
      if (file_exists($out)) return false;
      $in_file = fopen($file, "rb");
      if (!$out_file = gzopen($out, "wb".$level)) return false;
      while (!feof ($in_file))
      {
         $buffer = fread($in_file, 8192);
         if(!gzwrite($out_file, $buffer, 8192) > 0)
         {
            fclose($in_file);
            gzclose($out_file);
            return false;
         }
      }
      fclose($in_file);
      if (!gzclose($out_file)) return false;
      return true;
   }
   // extract date
   function at_date($digits)
   {
      if (!preg_match('/\d{12}/', $digits, $matches)) return NULL;
      return $matches[0];
   }
   $at_resume = $at_pause = $at_gzip = $at_dir_ok = false;
   $atf_ok = true;
   $at_interval = BACKUP_INTERVAL > 4 ? BACKUP_INTERVAL : 20;
   $at_archive = intval(BACKUP_SAVE_INTERVAL) ? intval(BACKUP_SAVE_INTERVAL) : 24;
   if (file_exists(DIR_FS_BACKUP . 'Auto Backup Write Failure.log')) // abort if true
   {
      at_message(AT_ERROR_LOG_EXIST,'',true,false,AT_NOCRON_BACKUP);
   }
   else
   {
      if (isset($_SESSION['backup_progress']) && is_array($_SESSION['backup_progress'])) // resuming?
      {
         $at_info = (object) $_SESSION['backup_progress'];
         if( tep_not_null($at_info->file) && file_exists(DIR_FS_BACKUP.$at_info->file))
         {
            $at_resume = true;
            $at_tables_done = $_SESSION['backup_tables'];
            $at_backup_db = 'backupnow';
            $at_saving = AT_RESUMING;
            $at_nobreak = false;
         }
         unset($_SESSION['backup_progress']);
         unset($_SESSION['backup_tables']);
      }
      if (!$at_resume)  // default operatons
      {
         $at_tables_done = array();
         $at_info = (object) array( 'rows' => '', 'data' => '', 'table' => '', 'file' => '', 'tc' => 0, 'dc' => 0, 'elapsed' => 0, 'diff' => '', 'last' => '', 'ref' =>  '');
         $at_backup_db = $at_saving = NULL;
         $at_nobreak = $cron ? true : (get_cfg_var('safe_mode') ? false : BACKUP_SPLIT != 'split');
      }
      $at_startSave = (float)at_microtime_float();
      $at_dc =& $at_info->dc;
      $at_tc =& $at_info->tc;
      $at_exclude_tables = array_flip(array_merge(array('sessions','whos_online'),$at_tables_done)); // add more if needed
      if ($at_maxruntime = (ini_get('max_execution_time'))) $at_maxruntime -=4; else $at_maxruntime = 26; // usually 30 secs
      //$at_maxruntime = 12;
      if ($at_nobreak)  set_time_limit(0);
      if ($cron) echo AT_TITLE . "\n\n" . CRON . AT_JOB . date(AT_DATETIME_FMT) . "\n\n";
      // check backup directory OK every 5 mins, rest only runs after
      if (( isset($_SESSION['backup_time']) && time() > $_SESSION['backup_time']) || $cron || !isset($_SESSION['backup_time']) || ($at_fs = ($_GET['test'] == 'at_backup')))
      {
         $_SESSION['backup_time'] = time() + 300;
         if (is_dir(DIR_FS_BACKUP))
            if (is_writeable(DIR_FS_BACKUP)) $at_dir_ok = true;
            else at_message(ERROR_BACKUP_DIRECTORY_NOT_WRITEABLE);
			else 
            at_message(@sprintf(ERROR_BACKUP_DIRECTORY_DOES_NOT_EXIST,($cron ? '' : '<a href="' . tep_href_link('auto_backup_setup.php') . '">')) . ($cron ? '' : '</a>'));
      }


      if ($at_dir_ok && !$at_resume) // read file dates
      {
         $at_dir = dir(DIR_FS_BACKUP);
         $at_contents = array();
         while ($at_file = $at_dir->read()) if (!is_dir(DIR_FS_BACKUP . $at_file)) $at_contents[]=array('file' => $at_file,'date' => at_date($at_file));
         $at_dir->close();
         if ($at_total = sizeof($at_contents))
         {
            usort($at_contents, 'at_comp');
            $at_entry = $at_contents[--$at_total]['date'];
            $at_last = $at_total ? $at_contents[$at_total-1]['date'] : 0;
            $at_info->ref = $at_contents[$at_total]['file'];
            $at_info->diff = (time() - strtotime($at_entry))/60;
            $at_info->last = (time() - strtotime($at_last))/60;
            if($at_info->diff>$at_interval)
            {
               $at_backup_db = 'backupnow';
               $at_saving = $at_entry ? @sprintf(AT_BACKUP_START, ($at_info->diff<60 ? '' : floor($at_info->diff/60) . AT_HOURS) . ($at_info->diff % 60)) : AT_BACKUP_FIRST;
            }
         }
         else
         {
            $at_backup_db = 'backupnow';
            $at_saving = AT_BACKUP_FIRST;
         }
      }

      if ($at_backup_db == 'backupnow') // backup/resume starts
      {
         at_message($at_saving,'warning');
         if (!$at_resume) $at_info->file = 'db_' . DB_DATABASE . '_' . date('YmdHi') . '.sql.part';
         $at_gzip_error = $at_tables = '';
         if ($at_fp = fopen(DIR_FS_BACKUP . $at_info->file, 'a'))
         {
            if (!$at_resume) // create header
            {
               $at_tables_query = tep_db_query('show tables');
               while ($at_tables_array = tep_db_fetch_array($at_tables_query))
               {
                  foreach ($at_tables_array as $at_table) if (!isset($at_exclude_tables[$at_table])) $at_tables.= $at_table . ', ' ;
               }
               $at_tables = substr_replace($at_tables, '', -2);
               $at_schema = AT_SCHEMA_HEAD . '# ' . date(AT_DATETIME_FMT) . "\n" . '# Backed up tables: ' . $at_tables . "\n";
               if (fputs($at_fp, $at_schema) == false) $atf_ok = false;
            }
            $at_tables_query = tep_db_query('show tables');
            while ($at_tables_array = tep_db_fetch_array($at_tables_query))
            {
               foreach ($at_tables_array as $at_table)
               {
                  if (isset($at_exclude_tables[$at_table])) continue;
                  $at_fields_query = tep_db_query("show fields from " . $at_table);
                  $at_field_list = array();
                  while ($at_fields = tep_db_fetch_array($at_fields_query)) $at_field_list[] = $at_fields['Field'];
                  if ( $at_join = ( $at_resume && $at_table == $at_info->table ))
                  {
                     $at_rows_query = tep_db_query("select `" . implode('`,`', $at_field_list) . "` from `" . $at_table . "` limit " . $at_info->rows . " ," . $at_info->data);
                     $at_schema = "\n\n" . '# pause/resume point' . "\n\n";
                  }
                  else
                  {
                     $at_tc ++; // create table
                     $at_schema = "\n" . 'drop table if exists `' . $at_table . '`;' . "\n";
                     $at_create_table_query = tep_db_query("show create table " . ($at_table));
                     $at_create_table_array = mysql_fetch_array($at_create_table_query, MYSQL_NUM);
                     $at_create_table = $at_create_table_array[1];
                     $at_create_table = substr($at_create_table, 0, strrpos($at_create_table, ')')-1);
                     $at_schema .= $at_create_table . ');' . "\n\n";
                     $at_rows_query = tep_db_query("select `" . implode('`,`', $at_field_list) . "` from `" . $at_table . "`");
                  }
                  if (fputs($at_fp, $at_schema) == false) $atf_ok = false; // write create table
                  // inserts if any data
                  if ($at_data = tep_db_num_rows($at_rows_query))
                  {
                     $at_schema = $iat_schema = 'insert into `' . $at_table . '` (`' . implode('`, `', $at_field_list) . '`) values';
                     $at_rows = 0;
                     $at_mod = (int)(140/sizeof($at_field_list));
                     // dump the data
                     while ($at_table_rows = tep_db_fetch_array($at_rows_query))
                     {
                        $at_elapsed = (float)at_microtime_float() - $at_startSave;
                        if (!$at_nobreak && $at_elapsed > $at_maxruntime && $at_rows) // break
                        {
                           $at_schema = substr_replace($at_schema,";",-1);
                           $at_info->elapsed += $at_elapsed;
                           if (fputs($at_fp, $at_schema) == false) $atf_ok = false;
                           $messageStack->add( @sprintf(AT_PAUSING, round($at_elapsed), number_format($at_tc), number_format( $at_dc + $at_rows + ( $at_join ? $at_info->rows : 0 ))), 'warning');
                           $_SESSION['backup_progress'] = array_merge(array( 'rows' => $at_rows + ( $at_join ? $at_info->rows : 0 ), 'data' => ( $at_join ?  $at_info->data : $at_data ), 'table' => $at_table), array_slice(get_object_vars($at_info), 3, NULL, true));
                           $_SESSION['backup_tables'] = $at_tables_done;
                           $at_pause = true;
                           break 3;
                        }
                        if ($at_rows && $at_rows%$at_mod == 0) $at_schema = substr_replace($at_schema,";\n",-1) . $iat_schema;  // split data @ ~140 fields
                        if (fputs($at_fp, $at_schema) == false) $atf_ok = false;
                        $at_schema = ' ('; // data starts
                        reset($at_field_list);
                        foreach($at_field_list as $at_i)
                        {
                           if (!isset($at_table_rows[$at_i]))
                           {
                              $at_schema .= 'NULL,';
                           }
                           elseif (tep_not_null($at_table_rows[$at_i]))
                           {
                              $at_row = addslashes($at_table_rows[$at_i]);
                              $at_row = str_replace("\n#", "\n".'\#', $at_row);
                              if (is_numeric($at_row)) $at_schema .= $at_row . ',';
                              else $at_schema .= '\'' . $at_row . '\',';
                           }
                           else
                           {
                              $at_schema .= '\'\',';
                           }
                        }		// foreach
                        $at_schema = substr_replace($at_schema,"),",-1); // replace last comma
                        $at_rows++;
                     }		// while
                     $at_schema = substr_replace($at_schema,";",-1) . "\n"; // data end
                     if (fputs($at_fp, $at_schema) == false) $atf_ok = false;
                     $at_dc += $at_rows + ( $at_join ? $at_info->rows : 0 );
                  }
                  $at_tables_done[] = $at_table;
                  if ($atf_ok === false) break 2;
               }		// foreach
            }		// while
            $at_endSave = (float)at_microtime_float();
            $at_backuptime = round($at_endSave - $at_startSave + $at_info->elapsed, 2);
            if(!$at_pause)
            {
               $at_schema = "\n\n" . '# ' . @sprintf(AT_COMPLETED, number_format($at_tc), number_format( $at_dc), $at_backuptime) . "\n\n";
               if (fputs($at_fp, $at_schema) == false) $atf_ok = false;
            }
            if (fclose($at_fp) === false) $atf_ok = false;
            if(!$at_pause && $atf_ok) 																	// success!
            {
               $at_backup_file = substr_replace($at_info->file,'',-5);
               if ($at_resume) $at_backup_file = str_replace(DB_DATABASE, DB_DATABASE . '_(split)', $at_backup_file);
               rename(DIR_FS_BACKUP.$at_info->file, DIR_FS_BACKUP.$at_backup_file);
               if (BACKUP_ZIP == 'gzip')
               {
                  if (at_gzip (DIR_FS_BACKUP . $at_backup_file) && file_exists(DIR_FS_BACKUP.$at_backup_file.'.gz') && filesize(DIR_FS_BACKUP.$at_backup_file.'.gz'))
                  {
                     unlink(DIR_FS_BACKUP.$at_backup_file);
                     $at_gzip = true;
                     $at_ziptime= round((float)at_microtime_float() - $at_endSave, 2);
                     $at_backup_file .= '.gz';
                  }
                  else
                  {
                     @unlink(DIR_FS_BACKUP.$at_backup_file.'.gz');
                     $at_gzip_error = AT_ERROR_GZIP;
                  }
               }
               if ($at_info->diff < ($at_archive * 60) && $at_info->last < ($at_archive * 60) && $at_info->ref <> '') // remove last if applies
               {
                  if (unlink(DIR_FS_BACKUP . $at_info->ref))
                  		$at_message = @sprintf( AT_DELETE_INTERIM_OK, $at_info->ref, date(AT_DATETIME_FMT,strtotime(at_date($at_info->ref))));
                  else 	$at_message = AT_DELETE_INTERIM_FAIL . $at_info->ref;
                  at_message($at_message,'warning',false, !$at_resume);
               }
               $at_database_saved = @sprintf( AT_BACKUP_OK, date(AT_DATETIME_FMT), number_format($at_tc), number_format($at_dc), $at_backuptime) . ($at_gzip ? @sprintf(AT_GZIP_OK, $at_ziptime) : '.');
               at_message($at_database_saved,'success',true, !$at_resume);
               if ($at_gzip_error) at_message($at_gzip_error,'',false, !$at_resume);
               if (BACKUP_MAIL_CRON == 'true' && $cron) include(DIR_WS_INCLUDES . 'mail_cron_backups.php');
            }
            elseif(!$atf_ok) // fail
            {
               @unlink(DIR_FS_BACKUP.$at_info->file);
               $at_message = @sprintf( AT_ERROR_WRITE, date(AT_DATETIME_FMT), $at_backuptime, number_format($at_tc));
               at_message($at_message,'error',true);
               if ($at_fp = fopen(DIR_FS_BACKUP . 'Auto Backup Write Failure.log', 'w'))
               {
                  fputs($at_fp, "\n\n" . $at_message);
                  fclose($at_fp);
               }
               else
               {
                  at_message(AT_ERROR_WRITE_FAIL_LOG);
               }
            }
         }
         else
         {
            at_message(AT_ERROR_WRITE_OPEN);
         }
      }
      elseif (($cron || $at_fs) && $at_dir_ok)
      {
         at_message(AT_NOCRON_BACKUP . @sprintf(AT_NOCRON_BACKUP2, $at_interval), 'success');
      }
      if ($at_dir_ok && AUTO_BACKUP_DELETE == 'true')
      {
         if (file_exists(DIR_FS_BACKUP.'removed_backup.log')) $at_logsdate = filemtime(DIR_FS_BACKUP . 'removed_backup.log');
         else $at_logsdate = true;
         if (($at_logsdate === true || $at_logsdate+(3600*24) < time() ) && file_exists(DIR_WS_INCLUDES . 'auto_backup_delete.php') ) include(DIR_WS_INCLUDES . 'auto_backup_delete.php');
      }
   }
?>